﻿using CSharp.ConsoleTools.ConHandlerEnums;
using CSharp.ConsoleTools.Properties;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CSharp.ConsoleTools
{
	#region event stuff
	#region         delegates
	public delegate void SizeChangedEventDelegate(object sender, SizeChangedEventArgs ea);
	public delegate void ContentChangedEventDelegate(object sender, ContentChangedEventArgs ea);
	#endregion

	#region         event args
	public class SizeChangedEventArgs : EventArgs
	{
	}

	public class ContentChangedEventArgs : EventArgs
	{
	}
	#endregion
	#endregion



	public class ConsoleArea
	{
		#region declares
        private int _intBorderTop = 0;
        private int _intBorderLeft = 0;
        private int _intBorderHeight = 10;
        private int _intBorderWidth = 80;

        private List<string> _lstContent;

		#region events
		public event SizeChangedEventDelegate SizeChangedEvent;
		protected void ThrowSizeChangedEvent(
			SizeChangedEventArgs ea)
		{
			if (SizeChangedEvent != null)
			{
				SizeChangedEvent(this, ea);
			}
		}

		public event ContentChangedEventDelegate ContentChangedEvent;
		protected void ThrowContentChangedEvent(
			ContentChangedEventArgs ea)
		{
			if (ContentChangedEvent != null)
			{
				ContentChangedEvent(this, ea);
			}
		}
		#endregion
		#endregion



		#region constructor
		//singleton private constructor
		public ConsoleArea()
		{
			_initVars();
			_initContentArray();
		}
		#endregion



		#region private functions
		private void _initVars()
		{
			/* default border set with double lines (decimal unicode numbers)
			 * ═: 9552
			 * ║: 9553
			 * ╔: 9556
			 * ╗: 9559
			 * ╚: 9562
			 * ╝: 9565 */
			BorderElements = new char[6] { '\u2550', '\u2551', '\u2554', '\u2557', '\u255A', '\u255D' };

			string be = new string(BorderElements);
			char[] cbe = be.ToCharArray();
			Settings.Default.AreaBorderSet = be;
			Settings.Default.Save();

			_autoSizeArea();

			this.ShowTitle = false;
			this.TitleText = "";
		}

		private void _autoSizeArea()
		{
			BorderWidth = Console.WindowWidth;
			BorderHeight = ((int)(Console.WindowHeight / 2)) - 1;
		}

		private void _initContentArray()
		{
			if (_lstContent == null) { _lstContent = new List<string>(); }
            _lstContent.Capacity = ContentHeight;
		}

		private void _renderBorder()
		{
			// generate and place upper border
			Console.SetCursorPosition(BorderLeft, BorderTop);
			string strHorizontalBorder = BorderElements[(int)enmBorderElementType.TopLeft].ToString().PadRight(BorderWidth - 2, BorderElements[(int)enmBorderElementType.Horizontal]) + BorderElements[(int)enmBorderElementType.TopRight].ToString();
			Console.Write(strHorizontalBorder);

			// place vertical borders
			for (int intTop = 1; intTop < BorderHeight - 1; intTop++)
			{
				Console.SetCursorPosition(BorderLeft, BorderTop + intTop);
				Console.Write(BorderElements[(int)enmBorderElementType.Vertical].ToString());
				Console.SetCursorPosition(BorderLeft + BorderWidth - 2, BorderTop + intTop);
				Console.Write(BorderElements[(int)enmBorderElementType.Vertical].ToString());
			}

			// generate and place lower border
			Console.SetCursorPosition(BorderLeft, BorderTop + BorderHeight - 1);
			strHorizontalBorder = BorderElements[(int)enmBorderElementType.BottomLeft].ToString().PadRight(BorderWidth - 2, BorderElements[(int)enmBorderElementType.Horizontal]) + BorderElements[(int)enmBorderElementType.BottomRight].ToString();
			Console.Write(strHorizontalBorder);

			// show title
			if (this.ShowTitle &&
				!string.IsNullOrEmpty(this.TitleText))
			{
				Console.SetCursorPosition(BorderLeft + 2, BorderTop);
				Console.Write(TitleText);
			}

			// place Cursor
			Console.SetCursorPosition(0, BorderTop + BorderHeight);
		}

		private void _renderContent()
		{
			if (!_contentIsEmpty())
			{
				//run through Lines and print it
				for (int idx = 0; idx < _lstContent.Count; idx++)
				{
                    //set cursor position
                    Console.SetCursorPosition(ContentLeft, ContentTop + idx);
                    
                    //output line
                    _outputContent(_lstContent[idx]);
				}
			}
		}

        private void _outputContent(
            string strOutput)
        {
            //check string
            if (!string.IsNullOrEmpty(strOutput))
            {
                strOutput = strOutput.Replace(Environment.NewLine, "");
                strOutput = strOutput.PadRight(ContentWidth - 1, ' ');

                if (strOutput.Length > ContentWidth - 1)
                {
                    strOutput = strOutput.Substring(0, ContentWidth - 1);
                }
            }

            Console.Write(strOutput);
        }
		#region helpers
		private bool _contentIsEmpty()
		{
			if (_lstContent == null)
			{
				return true;
			}

			foreach (string strLine in _lstContent)
			{
				if (!string.IsNullOrEmpty(strLine))
				{
					return false;
				}
			}

			return true;
		}

		private void _clearContent()
		{
			//clear content
            _lstContent.Clear();
            _initContentArray();

			//generate empty string
			string strEmpty = "";
			strEmpty = strEmpty.PadLeft(ContentWidth-1, ' ');
			
			for (int idx = 0; idx < ContentHeight; idx++)
			{
                //set cursor start position
                Console.SetCursorPosition(ContentLeft, ContentTop + idx);

                //output line
                _outputContent(strEmpty);
            }

            _renderContent();
		}
		#endregion
		#endregion



		#region interface implementation
		#region properties
		public int BorderTop
		{
			get { return _intBorderTop; }
			set
			{
				_intBorderTop = value;
				_initContentArray();
				ThrowSizeChangedEvent(new SizeChangedEventArgs());
			}
		}
		public int BorderLeft
		{
			get { return _intBorderLeft; }
			set
			{
				_intBorderLeft = value;
				_initContentArray();
				ThrowSizeChangedEvent(new SizeChangedEventArgs());
			}
		}
		public int BorderHeight
		{
			get { return _intBorderHeight; }
			set
			{
				_intBorderHeight = value;
				_initContentArray();
				ThrowSizeChangedEvent(new SizeChangedEventArgs());
			}
		}
		public int BorderWidth
		{
			get { return _intBorderWidth; }
			set
			{
				_intBorderWidth = value;
				_initContentArray();
				ThrowSizeChangedEvent(new SizeChangedEventArgs());
			}
		}

		public int ContentTop { get { return BorderTop + 1; } }
		public int ContentLeft { get { return BorderLeft + 1; } }
		public int ContentHeight { get { return BorderHeight -2; } }
		public int ContentWidth { get { return BorderWidth - 2; } }

		public char[] BorderElements { get; set; }

		public bool ShowTitle { get; set; }
		public string TitleText { get; set; }
		#endregion
		
		#region methods
		public void AutoSizeArea()
		{
			_autoSizeArea();
		}

		public void AddContent(
			string Content)
		{
			_lstContent.Add(Content);
			if (_lstContent.Count > ContentHeight)
			{
				_lstContent.RemoveAt(0);
			}
		}

		public bool RemoveContent(
			string Content)
		{
			return _lstContent.Remove(Content);
		}

		public void Render()
		{
			_renderBorder();
			_renderContent();
		}

		public void RenderContent()
		{
			_renderContent();
		}

		public void ClearContent()
		{
			_clearContent();
			_renderContent();
		}
		#endregion
		#endregion
	}
}
